﻿using System;
using System.Linq;
using System.Windows.Media;
using ChiWalker.Utilities;
using CommonBehaviors.Actions;
using Styx;
using Styx.CommonBot;
using Styx.CommonBot.POI;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;

namespace ChiWalker.Helpers
{
    internal static class Safers
    {
        /// <summary>
        ///     This behavior SHOULD be called at top of the combat behavior. This behavior won't let the rest of the combat
        ///     behavior to be called
        ///     if you don't have a target. Also it will find a proper target, if the current target is dead or you don't have a
        ///     target and still in combat.
        ///     Tank targeting is also dealed in this behavior.
        /// </summary>
        /// <returns></returns>
        public static Composite EnsureTarget()
        {
            return
                new Decorator(
                    new PrioritySelector(
                        EnsureAttackable(),
                        new PrioritySelector(
                            ctx =>
                            {
                                // Go below if current target is null or dead. We have other checks to deal with that
                                if (StyxWoW.Me.CurrentTarget == null || StyxWoW.Me.CurrentTarget.IsDead)
                                    return null;

                                // If the current target is in combat or has aggro towards us, it should be a valid target.
                                if (StyxWoW.Me.CurrentTarget.Combat || StyxWoW.Me.CurrentTarget.Aggro)
                                    return null;

                                // Check botpoi first and make sure our target is set to POI's object.
                                if (BotPoi.Current.Type == PoiType.Kill)
                                {
                                    WoWObject obj = BotPoi.Current.AsObject;

                                    if (obj != null)
                                    {
                                        if (StyxWoW.Me.CurrentTarget != obj)
                                            return obj;
                                    }
                                }

                                // Make sure we have the proper target from Targeting. 
                                // The Botbase should give us the best target in targeting.
                                WoWUnit firstUnit = Targeting.Instance.FirstUnit;

                                if (firstUnit != null)
                                {
                                    if (StyxWoW.Me.CurrentTarget != firstUnit)
                                        return firstUnit;
                                }

                                return null;
                            },
                            new Decorator(
                                ret => ret != null,
                                new Sequence(
                                    new Action(
                                        ret =>
                                            Logger.Write(Colors.Orange,
                                                "Current target is not the best target. Switching to " +
                                                ((WoWUnit) ret).Name + "!")),
                                    new Action(ret => ((WoWUnit) ret).Target()),
                                    new WaitContinue(
                                        2,
                                        ret => StyxWoW.Me.CurrentTarget != null &&
                                               StyxWoW.Me.CurrentTarget == (WoWUnit) ret,
                                        new ActionAlwaysSucceed())))),
                        new Decorator(
                            ret => StyxWoW.Me.CurrentTarget == null || StyxWoW.Me.CurrentTarget.IsDead,
                            new PrioritySelector(
                                ctx =>
                                {
                                    // If we have a RaF leader, then use its target.
                                    WoWPlayer rafLeader = RaFHelper.Leader;
                                    if (rafLeader != null && rafLeader.IsValid && !rafLeader.IsMe &&
                                        rafLeader.Combat &&
                                        rafLeader.CurrentTarget != null && rafLeader.CurrentTarget.IsAlive &&
                                        !Blacklist.Contains(rafLeader.CurrentTarget, BlacklistFlags.Combat))
                                    {
                                        return rafLeader;
                                    }

                                    // Check bot poi.
                                    if (BotPoi.Current.Type == PoiType.Kill)
                                    {
                                        var unit = BotPoi.Current.AsObject as WoWUnit;

                                        if (unit != null && unit.IsAlive && !unit.IsMe &&
                                            !Blacklist.Contains(unit, BlacklistFlags.Combat))
                                        {
                                            return unit;
                                        }
                                    }

                                    // Does the target list have anything in it? And is the unit in combat?
                                    // Make sure we only check target combat, if we're NOT in a BG. (Inside BGs, all targets are valid!!)
                                    WoWUnit firstUnit = Targeting.Instance.FirstUnit;
                                    if (firstUnit != null && firstUnit.IsAlive && !firstUnit.IsMe &&
                                        firstUnit.Combat &&
                                        !Blacklist.Contains(firstUnit, BlacklistFlags.Combat))
                                    {
                                        return firstUnit;
                                    }

                                    // Cache this query, since we'll be using it for 2 checks. No need to re-query it.
                                    WoWUnit agroMob =
                                        ObjectManager.GetObjectsOfType<WoWUnit>(false, false).
                                            Where(
                                                p =>
                                                    !Blacklist.Contains(p, BlacklistFlags.Combat) &&
                                                    p.IsHostile &&
                                                    !p.IsOnTransport &&
                                                    !p.IsDead &&
                                                    !p.Mounted && p.Distance <= Targeting.PullDistance &&
                                                    p.Combat).
                                            OrderBy(u => u.DistanceSqr).
                                            FirstOrDefault();

                                    if (agroMob != null)
                                    {
                                        // Return the closest one to us
                                        return agroMob;
                                    }

                                    // And there's nothing left, so just return null, kthx.
                                    return null;
                                },
                                // Make sure the target is VALID. If not, then ignore this next part. (Resolves some silly issues!)
                                new Decorator(
                                    ret => ret != null,
                                    new Sequence(
                                        new Action(
                                            ret =>
                                                Logger.Write(Colors.Orange,
                                                    "Current target is invalid. Switching to " +
                                                    ((WoWUnit) ret).Name + "!")),
                                        new Action(ret => ((WoWUnit) ret).Target()),
                                        new WaitContinue(
                                            2,
                                            ret => StyxWoW.Me.CurrentTarget != null &&
                                                   StyxWoW.Me.CurrentTarget == (WoWUnit) ret,
                                            new ActionAlwaysSucceed()))),
                                new ActionAlwaysSucceed()))));
        }

        public static Composite EnsureAttacking()
        {
            return new Decorator(ret => !StyxWoW.Me.IsAutoAttacking && StyxWoW.Me.CurrentTarget != null,
                new Action(ret =>
                {
                    StyxWoW.Me.ToggleAttack();
                    Logger.WriteVerbose("Started auto-attack");
                }));
        }

        public static Composite EnsureAttackable()
        {
            return
                new Decorator(
                    ret =>
                        StyxWoW.Me.CurrentTarget != null &&
                        (StyxWoW.Me.CurrentTarget.IsFriendly || StyxWoW.Me.CurrentTarget.IsDead ||
                         Blacklist.Contains(StyxWoW.Me.CurrentTarget.Guid, BlacklistFlags.Combat)),
                    new Action(ret =>
                    {
                        Logger.Write(Colors.Orange, "Blacklisting {0} as its friendly",
                            StyxWoW.Me.CurrentTarget.Name);
                        Blacklist.Add(StyxWoW.Me.CurrentTarget.Guid, BlacklistFlags.Combat,
                            TimeSpan.FromMinutes(10));
                        StyxWoW.Me.ClearTarget();
                        return RunStatus.Success;
                    }));
        }
    }
}